/*
 * Copyright 2006-2008 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.martinimix.service.payment.provider.cybersource;

import java.util.Collections;
import java.util.Map;

import net.martinimix.domain.order.Order;
import net.martinimix.domain.payment.CreditCard;
import net.martinimix.service.payment.provider.cybersource.context.CyberSourceServiceRequest;

/**
 * Provides an abstract CyberSource request adapter.
 * 
 * @author Scott Rossillo
 *
 */
public abstract class AbstractCyberSourceRequestAdapter implements CyberSourceRequestAdapter {
	
	/**
	 * The default value for sending shipping information as a line item to CyberSource;
	 * defaults to <code>true</code> 
	 */
	protected final static boolean DEFAULT_SEND_SHIPPING_LINE_ITEM = true;
	
	/**
	 * The default value of sending order totals to CyberSource;
	 * default to <code>true</code> 
	 */
	protected final static boolean DEFAULT_USE_ORDER_TOTAL = true;
	
	private Map authorizationRequestOptions;
	private boolean sendShippingLineItem = DEFAULT_SEND_SHIPPING_LINE_ITEM;
	private boolean useOrderTotal = DEFAULT_USE_ORDER_TOTAL;
	

	/**
	 * Creates a new CyberSource request adapter. 
	 */
	protected AbstractCyberSourceRequestAdapter() {
		super();
		authorizationRequestOptions = Collections.singletonMap("ccAuthService_run", "true");
	}

	/**
	 * Returns a CyberSource authorization request for the given credit card and order.
	 * This method delegates responsibility for building the actual request to 
	 * {@link #prepareAuthorizationRequest(CreditCard, Order)} and adds request options
	 * provided by the {@link #prepareAuthorizationRequestOptions(CreditCard, Order)} method. 
	 * 
	 * @see #prepareAuthorizationRequest(CreditCard, Order)
	 * @see #prepareAuthorizationRequestOptions(CreditCard, Order)
	 * @see net.martinimix.service.payment.provider.cybersource.CyberSourceRequestAdapter#createAuthorizationRequest(net.martinimix.domain.payment.CreditCard, net.martinimix.domain.order.Order)
	 */
	public final CyberSourceServiceRequest createAuthorizationRequest(CreditCard creditCard, Order order) {
		
		CyberSourceServiceRequest request = prepareAuthorizationRequest(creditCard, order);
		request.putAll(prepareAuthorizationRequestOptions(creditCard, order));
		
		return request;
	}
	
	/**
	 * Returns authorization request options for the given credit card and order.  By default this method
	 * simply returns the request options specified by {@link #getAuthorizationRequestOptions()}.  Subclasses
	 * may override this method to provide options on a request by request basis.  For example, some
	 * options may only be relevant for certain <code>creditCard</code> or <code>order</code> combinations. 
	 * 
	 * @param creditCard the <code>creditCard</code> being authorized
	 * @param order the <code>order</code> for which <code>creditCard</code> is being authorized for payment
	 * 
	 * @return a <code>Map</code> containing authorization request options for the given <code>creditCard</code>
	 * and <code>order</code>
	 */
	protected Map prepareAuthorizationRequestOptions(CreditCard creditCard, Order order) {
		return this.getAuthorizationRequestOptions();
	}
	
	/**
	 * Returns a CyberSource authorization request for the given credit card and order.
	 *  
	 * @param creditCard the <code>CreditCard</code> to authorize
	 * @param order the <code>Order</code> for which the <code>creditCard</code> will
	 * be authorized
	 * 
	 * @return a <code>CyberSourceServiceRequest</code> to authorize payment for the given 
	 * <code>creditCard</code> and <code>order</code>
	 */
	protected abstract CyberSourceServiceRequest prepareAuthorizationRequest(CreditCard creditCard, Order order);

	/**
	 * Returns a map containing configured authorization request options.
	 * 
	 * @return an immutable <code>Map</code> containing authorization request options
	 * 
	 * @see #setAuthorizationRequestOptions(Map)
	 */
	public Map getAuthorizationRequestOptions() {
		return authorizationRequestOptions;
	}
	
	/**
	 * Sets the request options to be passed to CyberSource on every
	 * authorization request.  If not set explicitly, only 
	 * <code>ccAuthService_run=true</code> is passed.
	 * 
	 * @param authorizationRequestOptions a <code>Map</code> containing
	 * authorization request options; may not be <code>null</code>
	 */
	public void setAuthorizationRequestOptions(Map authorizationRequestOptions) {
		this.authorizationRequestOptions = Collections.unmodifiableMap(authorizationRequestOptions);
	}
	
	/**
	 * Returns true if shipping information should be sent as an order
	 * line item.
	 * 
	 * @return <code>true</code> to send shipping information as a line
	 * item; <code>false</code> otherwise
	 */
	public boolean isSendShippingLineItem() {
		return sendShippingLineItem;
	}

	/**
	 * Sets whether shipping information is sent to CyberSource.
	 * 
	 * @param sendShippingLineItem <code>true</code> to send shipping information as a line
	 * item; <code>false</code> otherwise
	 */
	public void setSendShippingLineItem(boolean sendShippingLineItem) {
		this.sendShippingLineItem = sendShippingLineItem;
	}

	/**
	 * Returns true if the order total should be sent to CyberSource.
	 * 
	 * @return <code>true</code> to send the order total to CyberSource;
	 * <code>false</code> otherwise
	 */
	public boolean isUseOrderTotal() {
		return useOrderTotal;
	}

	/**
	 * Sets whether the order total should be sent to CyberSource
	 * 
	 * @param useOrderTotal <code>true</code> to send the order total to CyberSource;
	 * <code>false</code> otherwise
	 */
	public void setUseOrderTotal(boolean useOrderTotal) {
		this.useOrderTotal = useOrderTotal;
	}

}